using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using Ionic.Crc;
using UnityEditor;
using UnityEngine;

namespace Editor
{
    public class BundleEditor : UnityEditor.Editor
    {
        private static string _bundleTargetPath = Application.streamingAssetsPath;
        
        //AssetBundle 配置文件路径
        private static string _abConfigPath = "Assets/Editor/ABConfig.asset";
        
        //private static string _abBytePath = ;

        //key：ab包名   value：路径         
        private static Dictionary<string, string> _allFileDir = new Dictionary<string, string>();

        // 保存所有已处理资源的路径 用于 过滤 防止冗余
        private static HashSet<string> _allFileAb = new HashSet<string>();

        // key: Prefab 名称 , value: Prefab 依赖资源的路径列表
        private static Dictionary<string, List<string>> _allPrefabDir = new Dictionary<string, List<string>>();

        //储存 有效的 路径
        private static HashSet<string> _configFil = new HashSet<string>();
        
        [MenuItem("Tools/1、打包")]
        public static void Build()
        {
            try
            {
                //清除 之前 数据
                ClearPreviousData();
            
                //加载 配置 文件  并  设置对应 资源ab包名
                LoadAbConfigSetAbName();
            
                //创建 资源 配置 文件
                ConfigTable();
            
                //打包
                BuildAssetBundle();
                
                //清除 ab包名
                RemoveOldAssetBundleNames();
            }
            catch (System.Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }

        /// <summary>
        /// 删除 之前 存在 并 本次打包 不在用的 包
        /// </summary>
        static void DeleteAb()
        {
            string[] allBundleName = AssetDatabase.GetAllAssetBundleNames();
            DirectoryInfo direction = new DirectoryInfo(_bundleTargetPath);
            FileInfo[] files = direction.GetFiles("*", SearchOption.AllDirectories);
            foreach (var item in files)
            {
                if (ContainAbName(item.Name,allBundleName) || item.Name.EndsWith(".meta"))
                {
                    continue;
                }
                else
                {
                    Debug.Log("此AB包名已经被删除或者改名：" + item.Name);
                    if (File.Exists(item.FullName))
                    {
                        File.Delete(item.FullName);
                    }
                }
            }
        }

        static bool ContainAbName(string itemName, string[] allBundleName)
        {
            foreach (var item in allBundleName)
            {
                if (itemName == item)
                {
                    return true;
                }
            }
            return false;
        }

        static void ConfigTable()  
        {
            string[] allBundles = AssetDatabase.GetAllAssetBundleNames();

            //key:全路径   value:包名
            Dictionary<string, string> resPathDic = new Dictionary<string, string>();

            foreach (var abName in allBundles)
            {
                //Debug.LogError(" / 1      " + abName);
                string[] allBundlePath = AssetDatabase.GetAssetPathsFromAssetBundle(abName);
                foreach (var path in allBundlePath)
                {
                    if (path.EndsWith(".cs") || !ValidPath(path))
                    {
                        continue;
                    }
                    //Debug.LogError(" / 2     " + path);
                    resPathDic.Add(path, abName);
                }
            }

            DeleteAb();

            WriteData(resPathDic);
        }

        /// <summary>
        /// 创建 xml 配置文件
        /// </summary>
        /// <param name="resPathDic"></param>
        static void WriteData(Dictionary<string, string> resPathDic)
        {
            AssetBundleConfig config = new AssetBundleConfig();
            config.AbList = new List<AbBase>();
            foreach (var path in resPathDic.Keys)
            {
                AbBase abBase = new AbBase();
                abBase.Path = path;
                abBase.Crc = CRC32.GetCRC32(path);
                abBase.AbName = resPathDic[path];
                abBase.AssetName = path.Remove(0, path.LastIndexOf("/") + 1);
                abBase.AbDepends = new List<string>();
                string[] resDepends = AssetDatabase.GetDependencies(path);
                foreach (var depend in resDepends)
                {
                    if (depend == path || path.EndsWith(".cs"))
                    {
                        continue;
                    }

                    string abName = "";
                    if (resPathDic.TryGetValue(depend,out abName))
                    {
                        if (abName == resPathDic[path])
                        {
                            continue;
                        }
                        if (!abBase.AbDepends.Contains(abName))
                        {
                            abBase.AbDepends.Add(abName);
                        }
                    }
                }
                config.AbList.Add(abBase);
            }

            CreateXml(config);
            CreateByte(config);
        }

        /// <summary>
        /// 创建资源列表对应关系  xml文件
        /// </summary>
        /// <param name="assetBundleConfig"></param>
        static void CreateXml(AssetBundleConfig assetBundleConfig)
        {
            string xmlPath = Application.dataPath + "/AssetBundleConfig.xml";
            if (File.Exists(xmlPath))
            {
                File.Delete(xmlPath);
            }
            FileStream fileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
            StreamWriter sw = new StreamWriter(fileStream, System.Text.Encoding.UTF8);
            XmlSerializer xs = new XmlSerializer(assetBundleConfig.GetType());
            xs.Serialize(sw,assetBundleConfig);
            sw.Close();
            fileStream.Close();
        }
        
        /// <summary>
        /// 创建 字节数据 资源对应列表 数据
        /// </summary>
        /// <param name="assetBundleConfig"></param>
        static void CreateByte(AssetBundleConfig assetBundleConfig)
        {
            foreach (var item in assetBundleConfig.AbList)
            {
                item.Path = string.Empty;
            }
            string bytePath = "Assets/GameData/Data/ABData/AssetBundleConfig.bytes";
            FileStream fs = new FileStream(bytePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, assetBundleConfig);
            fs.Close();
        }
        
        /// <summary>
        /// 打包 
        /// </summary>
        static void BuildAssetBundle()
        {
            BuildPipeline.BuildAssetBundles(_bundleTargetPath, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);
        }
        
        /// <summary>
        /// 加载 ABConfig 并处理文件夹和文件路径 设置包名
        /// </summary>
        static void LoadAbConfigSetAbName()
        {
            ABConfig abConfig = AssetDatabase.LoadAssetAtPath<ABConfig>(_abConfigPath);

            foreach (var item in abConfig.mAllFileDirAb)
            {
                if (_allFileDir.ContainsKey(item.abName))
                {
                    Debug.LogError("AB包配置名字重复,请检查！");
                }
                else
                {
                    _allFileDir.Add(item.abName,item.path);
                    _allFileAb.Add(item.path);
                    _configFil.Add(item.path);
                }
            }

            string[] allStr = AssetDatabase.FindAssets("t:Prefab", abConfig.mAllPrefabPath.ToArray());

            for (int i = 0; i < allStr.Length; i++)
            {
                string path = AssetDatabase.GUIDToAssetPath(allStr[i]);
                _configFil.Add(path);
                EditorUtility.DisplayProgressBar("查找Prefab", "Prefab:" + path, i * 1.0f / allStr.Length);
                if (!ContainAllFileAb(path))
                {
                    ProcessPrefab(path);
                }
            }
            
            foreach (var item in _allFileDir)
            {
                SetAbName(item.Key,item.Value);
            }

            foreach (var item in _allPrefabDir)
            {
                SetAbName(item.Key,item.Value);
            }
        }
        
        /// <summary>
        /// 处理单个 Prefab  和  此 依赖项
        /// </summary>
        /// <param name="path"></param>
        static void ProcessPrefab(string path)
        {
            GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(path);
            string[] allDepend = AssetDatabase.GetDependencies(path);
            var allDependPath = new List<string> { path };

            foreach (var item in allDepend)
            {
                if (!ContainAllFileAb(item) && !item.EndsWith(".cs")) 
                {
                    _allFileAb.Add(item);
                    allDependPath.Add(item);
                }
            }

            if (_allPrefabDir.ContainsKey(obj.name))
            {
                Debug.LogError("存在相同名字的Prefab！" + obj.name);
            }
            else
            {
                _allPrefabDir.Add(obj.name, allDependPath);
            }
        }
        
        /// <summary>
        /// 移除所有旧的 AssetBundle 名称
        /// </summary>
        static void RemoveOldAssetBundleNames()
        {
            string[] oldAbNames = AssetDatabase.GetAllAssetBundleNames();

            for (int i = 0; i < oldAbNames.Length; i++)
            {
                AssetDatabase.RemoveAssetBundleName(oldAbNames[i], true);
                EditorUtility.DisplayProgressBar("清除AB包名", "名字：" + oldAbNames[i], i * 1.0f / oldAbNames.Length);
            }
            
            AssetDatabase.Refresh();
            
            EditorUtility.ClearProgressBar();
        }

        static void SetAbName(string name,string path)
        {
            AssetImporter assetImporter = AssetImporter.GetAtPath(path);
            if (assetImporter == null)
            {
                Debug.LogError("不存在此路径文件：" + path);
            }
            else
            {
                assetImporter.assetBundleName = name;
            }
        }

        static void SetAbName(string name,List<string> paths)
        {
            foreach (var item in paths)
            {
                SetAbName(name, item);
            }
        }
        
        /// <summary>
        /// 判断 该 文件 是否 已经加入 ab包
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        static bool ContainAllFileAb(string path)
        {
            foreach (var item in _allFileAb)
            {
                if (item == path || (path.Contains(item) && path.Replace(item, "")[0] == '/')) 
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 是否有效路径
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        static bool ValidPath(string path)
        {
            foreach (var item in _configFil)
            {
                if (path.Contains(item))
                {
                    return true;
                }
            }
            return false;
        }
        
        // 清除之前的数据
        static void ClearPreviousData()
        {
            _allFileDir.Clear();
            _allFileAb.Clear();
            _allPrefabDir.Clear();
            _configFil.Clear();
        }
    }
}